package com.example.springboot0507.basic.util;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalUnit;
import java.util.Date;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Java8 日期工具类
 */
public class LocalDateUtil {

    /**
     * Date转换为LocalDateTime.
     *
     * @param date
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime convertDateToLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * String Date转换为LocalDateTime.
     *
     * @param date
     * @return
     */
    public static LocalDateTime convertStrToLocalDateTime(String date) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return LocalDateTime.parse(date, df);
    }

    /**
     * 将yyyy-MM格式的字符串转成yyyy-MM-dd格式的LocalDate
     *
     * @param dateStr
     * @return
     */
    public static LocalDate convertStrToLocalDate(String dateStr) {
        // 定义一个正则表达式来匹配 "yyyy-mm" 格式的字符串
        Pattern pattern = Pattern.compile("^(\\d{4})-(\\d{2})$");
        Matcher matcher = pattern.matcher(dateStr);

        if (!matcher.matches()) {
            throw new IllegalArgumentException("Invalid date format. Expected 'yyyy-mm' but got: " + dateStr);
        }

        // 提取年份和月份
        int year = Integer.parseInt(matcher.group(1));
        int month = Integer.parseInt(matcher.group(2));

        // 验证月份是否在1-12的范围内
        if (month < 1 || month > 12) {
            throw new IllegalArgumentException("Invalid month. Expected 1-12 but got: " + month);
        }

        // 使用年份、月份和假设的日（1号）创建 LocalDate 对象
        return LocalDate.of(year, month, 1);
    }

    /**
     * 时间格式转换
     *
     * @param dateTime
     * @return
     */
    public static LocalDateTime format(String dateTime) {
        if (StrUtil.isBlank(dateTime)) return null;
        return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN));
    }

    /**
     * 时间格式转换
     *
     * @param date
     * @return
     */
    public static LocalDate formatLocalDate(String date) {
        if (StrUtil.isBlank(date)) return null;
        return LocalDate.parse(date, DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));
    }

    /**
     * LocalDateTime转换为Date.
     *
     * @param time
     * @return java.util.Date
     */
    public static Date convertLocalDateTimeToDate(LocalDateTime time) {
        if (Objects.isNull(time)) return null;
        return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
    }


    /**
     * 获取指定日期的毫秒.
     *
     * @param time
     * @return java.lang.Long
     */
    public static Long getMilliByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定日期的秒.
     *
     * @param time
     * @return java.lang.Long
     */
    public static Long getSecondsByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
    }

    /**
     * 获取指定时间的指定格式.
     *
     * @param time
     * @param pattern
     * @return java.lang.String
     */
    public static String formatTime(LocalDateTime time, String pattern) {
        return time.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取指定日期的日期字符串.
     *
     * @param date
     * @param pattern
     * @return
     */
    public static String formatDate(LocalDate date, String pattern) {
        return date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化时间
     *
     * @param time
     * @return
     */
    public static String formatTime(LocalDateTime time) {
        return time.format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN));
    }

    /**
     * 获取当前时间的指定格式.
     *
     * @param pattern
     * @return java.lang.String
     */
    public static String formatNow(String pattern) {
        return formatTime(LocalDateTime.now(), pattern);
    }

    /**
     * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*.
     *
     * @param time
     * @param number
     * @param field
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
        return time.plus(number, field);
    }

    /**
     * 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*.
     *
     * @param time
     * @param number
     * @param field
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field) {
        return time.minus(number, field);
    }

    /**
     * 获取两个日期的差  field参数为ChronoUnit.*.
     *
     * @param startTime
     * @param endTime
     * @param field
     * @return long
     */
    public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
        Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
        if (field == ChronoUnit.YEARS) {
            return period.getYears();
        }
        if (field == ChronoUnit.MONTHS) {
            return period.getYears() * 12L + period.getMonths();
        }
        return field.between(startTime, endTime);
    }

    /**
     * 获取一天的开始时间，2017,7,22 00:00.
     *
     * @param time
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getDayStart(LocalDateTime time) {
        return time.withHour(00)
                .withMinute(00)
                .withSecond(00)
                .withNano(0);
    }

    /**
     * 获取一天的结束时间，2017,7,22 23:59:59.999999999.
     *
     * @param time
     * @return java.time.LocalDateTime
     */
    public static LocalDateTime getDayEnd(LocalDateTime time) {
        return time.withHour(23)
                .withMinute(59)
                .withSecond(59)
                .withNano(0);
    }

    /**
     * 得到两个日期时间的差额(分)
     * 多1秒向上入为分
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 两个时间相差多少分
     */
    public static long diffDateMinutes(LocalDateTime startTime, LocalDateTime endTime) {
        if (Objects.isNull(startTime)) return 0L;
        long seconds = LocalDateUtil.betweenTwoTime(startTime, endTime, ChronoUnit.SECONDS);
        Duration duration = Duration.ofMinutes(BigDecimal.valueOf(seconds).divide(BigDecimal.valueOf(60), RoundingMode.UP).longValue());
        return duration.toMinutes();
    }

    /**
     * 得到两个日期时间的差额(秒)
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 两个时间相差多少分
     */
    public static long diffDateSeconds(LocalDateTime startTime, LocalDateTime endTime) {
        if (Objects.isNull(startTime)) return 0L;
        return Duration.between(startTime, endTime).getSeconds();
    }

    /**
     * 得到两个日期时间的差额(小时)
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 两个时间相差多少小时
     */
    public static long differenceDateHours(LocalDateTime startTime, LocalDateTime endTime) {
        Duration between = Duration.between(startTime, endTime);
        return between.toHours();
    }

    /**
     * 得到两个日期时间的差额(天)
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 两个时间相差多少天
     */
    public static long differenceDateDays(LocalDateTime startTime, LocalDateTime endTime) {
        Duration between = Duration.between(startTime, endTime);
        return between.toDays();
    }

    public static Integer differenceDateYears(LocalDateTime startTime, LocalDateTime endTime) {
        Duration between = Duration.between(startTime, endTime);
        return Math.toIntExact(between.toDays() / 365);
    }

    /**
     * LocalDateTime 转化为 LocalDate
     *
     * @param localDateTime 输入的时间
     * @return LocalDate的日期类型
     */
    public static LocalDate formatLocalDate(LocalDateTime localDateTime) {
        return localDateTime.toLocalDate();
    }


    /**
     * LocalDate 转化为 LocalDateTime
     *
     * @param localDate 输入的时间
     * @return LocalDateTime的日期类型
     */
    public static LocalDateTime localDateTime2LocalDate(LocalDate localDate) {
        return localDate.atStartOfDay(ZoneOffset.ofHours(8)).toLocalDateTime();
    }

    /**
     * Date类型转LocalDate类型
     *
     * @param date
     * @return
     */
    public static LocalDate dateToLocalDate(Date date) {
        if (date instanceof java.sql.Date){
            return ((java.sql.Date)date).toLocalDate();
        }
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDate localPriceDate = instant.atZone(zoneId).toLocalDate();
        return localPriceDate;
    }

    /**
     * LocalDate类型转Date类型
     *
     * @param localDate
     * @return Date
     */
    public static Date localDateToDate(LocalDate localDate) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDate.atStartOfDay(zoneId);
        Date date = Date.from(zdt.toInstant());
        return date;
    }

    /**
     * 转换YearMonth为LocalDate
     */
    public static LocalDate yearMonth2LocalDate(YearMonth yearMonth) {
        return LocalDate.of(yearMonth.getYear(), yearMonth.getMonthValue(), 1);
    }

    /**
     * 转换LocalDate为YearMonth
     */
    public static YearMonth localDate2YearMonth(LocalDate localDate) {
        return YearMonth.of(localDate.getYear(), localDate.getMonthValue());
    }

    /**
     * 获取某月开始日期
     *
     * @param yearMonth
     * @return
     */
    public static LocalDate getBeginMonth(YearMonth yearMonth) {
        return yearMonth2LocalDate(yearMonth).with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取某月开始日期
     *
     * @param localDate
     * @return
     */
    public static LocalDate getBeginMonth(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取某月结束日期
     *
     * @param localDate
     * @return
     */
    public static LocalDate getEndMonth(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取某月结束日期
     *
     * @param yearMonth
     * @return
     */
    public static LocalDate getEndMonth(YearMonth yearMonth) {
        return yearMonth2LocalDate(yearMonth).with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取某月开始时间
     *
     * @param localDate
     * @return
     */
    public static LocalDateTime getBeginTime(LocalDate localDate) {
        return getDayStart(localDateTime2LocalDate(localDate));
    }

    /**
     * 获取某月结束时间
     *
     * @param localDate
     * @return
     */
    public static LocalDateTime getEndTime(LocalDate localDate) {
        return getDayEnd(localDateTime2LocalDate(localDate));
    }



    /**
     * 时间戳转LocalDateTime
     *
     * @param timestamp
     * @return
     */
    public static LocalDateTime timestampToDatetime(long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * 当前时间戳
     *
     * @return
     */
    public static String currentTimestamp() {
        return formatNow(DatePattern.PURE_DATETIME_MS_PATTERN);
    }

    /**
     * 时间戳转换为 -年月日 例如 2024年2月9日
     *
     * @param dateTime 时间戳
     * @return
     */
    public static String formatDateWithChineseStr(LocalDateTime dateTime) {
        int year = dateTime.getYear();
        int month = dateTime.getMonthValue();
        int day = dateTime.getDayOfMonth();
        return year + "年" + month + "月" + day + "日";
    }

    /**
     * 获取昨天起始时间
     *
     * @param nowTime 时间维度值
     * @return
     */
    public static LocalDateTime getYesterdayMinTime(LocalDateTime nowTime) {
        // 设置昨天的开始时间为00:00:00
        return nowTime.with(LocalTime.MIN);
    }

    /**
     * 获取昨天半夜12点时间
     *
     * @param nowTime 时间维度值
     * @return
     */
    public static LocalDateTime getYesterdayMaxTime(LocalDateTime nowTime) {
        // 设置昨天的结束时间为23:59:59
        return nowTime.with(LocalTime.MAX);
    }

    private static final Pattern DATE_PATTERN = Pattern.compile(
            "(\\d{4})[-/](\\d{1,2})[-/](\\d{1,2})\\s*(\\d{1,2}|2[0-3]):([0-5]?\\d):?([0-5]?\\d)?"
    );

    /**
     * 日期格式转换
     * yyyy-MM-dd HH:mm:ss|yyyy-M-d H:mm ...
     *
     * @param dateString 日期字符串
     * @return 日期
     */
    public static LocalDateTime parseWithFormatters(String dateString) {
        Matcher matcher = DATE_PATTERN.matcher(dateString);
        if (matcher.matches()) {
            String yearStr = matcher.group(1);
            String monthStr = matcher.group(2).length() == 1 ? "0" + matcher.group(2) : matcher.group(2);
            String dayStr = matcher.group(3).length() == 1 ? "0" + matcher.group(3) : matcher.group(3);
            String hourStr = matcher.group(4).length() == 1 ? "0" + matcher.group(4) : matcher.group(4);
            String minuteStr = matcher.group(5).length() == 1 ? "0" + matcher.group(5) : matcher.group(5);
            String secondStr = matcher.group(6) != null ? (matcher.group(6).length() == 1 ? "0" + matcher.group(6) : matcher.group(6)) : "00";
            String format = "%s-%s-%s %s:%s:%s";
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(String.format(format, "yyyy", "MM", "dd", "HH", "mm", "ss"));
            return LocalDateTime.parse(String.format(format, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr), formatter);
        } else {
            throw new IllegalArgumentException("Unable to parse date-time string '" + dateString);
        }
    }

    /**
     * 判断是否在两个时间中间
     *
     * @param dateToCheck
     * @param startDate
     * @param endDate
     * @return
     */
    public static boolean isDateBetween(LocalDate dateToCheck, LocalDate startDate, LocalDate endDate) {
        return !dateToCheck.isBefore(startDate) && !dateToCheck.isAfter(endDate);
    }

    /**
     * 将秒转成北京时间的年月日时分秒
     * @param seconds
     * @return
     */
    public static LocalDateTime convertSecond2Time(Long seconds) {
        if (null == seconds) {
            return null;
        }
        return LocalDateTime.ofEpochSecond(seconds, 0, ZoneOffset.ofHours(8));
    }
}
